 package erfgame.core.event.queue.memory;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import erfgame.core.event.Event;
import erfgame.core.event.EventQueue;
import erfgame.core.event.EventQueueListener;

public class InMemoryEventQueue implements EventQueue, Serializable {

	private static final long serialVersionUID = 0;
	
	private List<Event> events;
	private transient List<EventQueueListener> listeners;
	
	private static final Comparator<Event> COMPARATOR = new Comparator<Event>() {
		public int compare(Event e1, Event e2) {
			return (int)(e2.getTime() - e1.getTime());
		}
	};
	
	public InMemoryEventQueue() {
		this.events = new ArrayList<Event>();
	}
	
	public void addEventQueueListener( EventQueueListener listener ) {
		if( this.listeners == null ) {
			this.listeners = new ArrayList<EventQueueListener>();
		}
		this.listeners.add( listener );
	}
	
	public void removeEventQueueListener( EventQueueListener listener ) {
		if( this.listeners != null ) {
			this.listeners.remove( listener );
		}
	}
	
	public void addEvent(Event event) {
		int index = Collections.binarySearch(
				this.events, 
				event, 
				COMPARATOR
		);
		if( index < 0 ) {
			index = -(index + 1);
		} else {
			if( this.events.get(index).getAction() == event.getAction() ) {
				throw new RuntimeException( "event added twice!" );
			}
		}
		this.events.add( index, event );
		if( index == 0 ) {
			this.fireHeadAdded(event);
		}
	}
	
	public void removeEvent(Event event) {
		// TODO: binary search to remove
		// fire head changed event if it was the first
		int index = this.events.indexOf( event );
		if( index >= 0 ) {
			this.events.remove( event );			
			if( index == 0 ) {
				this.fireHeadAdded( peek() );
			}
		}
	}

	public Event peek() {
		if( this.events.size() > 0 ) {
			return this.events.get( this.events.size()-1 );
		} else {
			return null;
		}
	}

	public Event pop() {
		// do not fire head changed event?
		if( this.events.size() > 0 ) {
			return this.events.remove( this.events.size()-1 );
		} else {
			return null;
		}
	}

	public boolean isEmpty() {
		return this.events.isEmpty();
	}
	
	public List<Event> getEvents() {
		return this.events;
	}
	
	protected void fireHeadAdded( Event e ) {
		for( int i=this.listeners.size(); i>0; ) {
			i--;
			EventQueueListener listener;
			listener = this.listeners.get( i );
			listener.headChanged(e);
		}
	}
}
